/*
 * Marmoset Add-In for Microsoft Visual Studio
 *
 * Copyright (C) 2007, David H. Hovemeyer
 * Copyright (C) 2007, Fidel K. Estrella
 *
 * This program is free software licensed under the
 * terms of the GNU General Public License, version 2.
 * See the file "COPYING.txt" for details.
 */

using System;
using System.Collections.Generic;
using System.Data;
using System.IO;
using System.Windows.Forms;
using System.Text;
using Extensibility;
using EnvDTE;
using EnvDTE80;
using ICSharpCode.SharpZipLib.Core;
using ICSharpCode.SharpZipLib.Zip;

namespace Marmoset
{
    /// <summary>
    /// The Importer handles unpackaging a zip archive containing a Visual Studion 2005 solution
    /// and places the entire contents of the solution into a folder within
    /// %My Documents%\Visual Studio 2005\Projects.
    /// </summary>
    class Importer
    {
        #region Readonly strings
        private static readonly string ERROR_DIALOG_TITLE = "Import error";
        #endregion

        #region Private Fields
        /// <summary>
        /// Private object fields for Importer object.
        /// </summary>
        private DebugOutput _debugOut;
        private string _importFileName;
        private DTE2 _applicationObject;
        private string _solutionFileName;
        #endregion

        #region Constructor
        /// <summary>
        /// Constructor for the Importer object.
        /// </summary>
        /// <param name="appObj">Current application object (Visual Studio).</param>
        public Importer(DTE2 appObj)
        {
            _applicationObject = appObj;
        }
        #endregion

        #region Public Members

        /// <summary>
        /// Allows Importer object control of output pane for debugging.
        /// </summary>
        /// <param name="debugOut">Debug output object from the application object (Visual Studio).</param>
        public void SetDebugOutput(DebugOutput debugOut)
        {
            this._debugOut = debugOut;
        }

        /// <summary>
        /// Opens a dialog that allows the user to select a zip archive containing a solution for import.
        /// </summary>
        /// <returns>True if a file has been chosen, false if canceled.</returns>
        public bool ChooseImportFile()
        {
            OpenFileDialog dialog = new OpenFileDialog();
            dialog.Filter = "zip files (*.zip)|*.zip";
            dialog.FilterIndex = 0;
            dialog.RestoreDirectory = true;

            if (dialog.ShowDialog() != DialogResult.OK)
            {
                return false;
            }

            _importFileName = dialog.FileName;
            return true;
        }

        /// <summary>
        /// Getter for file name of the zip archive.
        /// </summary>
        /// <returns>Full path of the selected zip archive, null if none selected.</returns>
        public string GetImportFileName()
        {
            return _importFileName;
        }

        /// <summary>
        /// Unarchives and places a solution/project into a folder of the same name as the given valid
        /// zip archive.
        /// </summary>
 
        public void Import()
        {
            // Set up a FastZipEvents object to observe extracted file names
            FastZipEvents fastZipEvents = new FastZipEvents();
            fastZipEvents.ProcessFile = new ProcessFileDelegate(this.OnProcessFile);

            FastZip importer = new FastZip(fastZipEvents);
            
            //TODO: Find a way, if any, to simplify finding the name of the archive (minus the .zip extension).
            string importFolderName =
                _importFileName.Substring(_importFileName.LastIndexOf("\\") + 1,
                                            _importFileName.Length - (_importFileName.LastIndexOf("\\")) - 5);

            //
            // FIXME: I could not find a good way to determine
            // what the Visual Studio user directory is called.
            // So, as a hack, we check the Version, and
            // use it to distinguish between VS 2005 and VS 2008.
            //
            string vsVersionName;
            if (_applicationObject.Version == "9.0")
            {
                vsVersionName = "Visual Studio 2008";
            }
            else
            {
                vsVersionName = "Visual Studio 2005";
            }

            string targProjDir = Environment.GetFolderPath(Environment.SpecialFolder.Personal) +
                "\\" + vsVersionName + "\\Projects\\" + importFolderName;
            
            // Check to make sure that the directory we're going to
            // extract the solution into doesn't already exist.
            if (Directory.Exists(targProjDir))
            {
                Dialogs.ShowErrorDialog(
                    ERROR_DIALOG_TITLE,
                    "Cannot import solution -- solution directory with same name already exists");
                return;
            }

            //Creates the project directory, then zips to that directory.
            Directory.CreateDirectory(targProjDir);

            try
            {
                importer.ExtractZip(_importFileName, targProjDir, null);

                if (_solutionFileName == null)
                {
                    Dialogs.ShowErrorDialog(ERROR_DIALOG_TITLE, "The extracted zip file does not contain a Visual Studio solution.");
                    return;
                }

                Dialogs.ShowInfoDialog("Import", "Import successful! Solution imported to " + targProjDir);

                // Open the solution
                string fullSolutionPath = targProjDir + Path.DirectorySeparatorChar + _solutionFileName;
                WriteToOutput("Import: full solution file path appears to be " + fullSolutionPath);
                FindAndOpenSolution(fullSolutionPath);
            }
            catch (Exception e)
            {
                Dialogs.ShowErrorDialog(ERROR_DIALOG_TITLE, "Error importing solution", e);
            }
        }

        #endregion

        #region Private Members

        /// <summary>
        /// Callback method (delegate) to observe files extracted from
        /// the imported zip archive.
        /// </summary>
        /// <param name="sender">the FastZipEvents object</param>
        /// <param name="e">ScanEventArgs describing the file being extracted</param>
        private void OnProcessFile(object sender, ScanEventArgs e)
        {
            if (e.Name.ToLower().EndsWith(".sln"))
            {
                _solutionFileName = e.Name;
                WriteToOutput("Import: solution filename appears to be " + _solutionFileName);
            }
        }

        /// <summary>
        /// Sends a debugging message to output pane.
        /// </summary>
        /// <param name="message">Debug message to be sent.</param>
        private void WriteToOutput(string message)
        {
            if (_debugOut != null)
            {
                _debugOut.WriteToOutput(message);
            }
        }

        /// <summary>
        /// Programmatically opens the unzipped solution within Visual Studio.
        /// </summary>
        /// <param name="path"></param>
        private void FindAndOpenSolution(string path)
        {
            this._applicationObject.Solution.Open(path);
        }
        #endregion
    }
}
